# Table of Contents

# 액티비티

안드로이드 애플리케이션은 하나 이상의 액티비티로 구성됩니다. 액티비티(Activity)는 애플리케이션을 구성하는 구성요소입니다. 액티비티는 하나의 사용자 인터페이스 화면 및 그것의 기능으로 구성됩니다.

예를 들어 다음 메모장 앱을 살펴봅시다.

이 메모장 앱은 목록을 보여주는 화면글을 작성하는 화면으로 구성되어있습니다. 따라서 두 개의 액티비티로 구성되어있습니다.

액티비티는 사용자 인터페이스 화면을 담당하는 xml 파일과 화면의 기능을 명세하는 .kt 파일 (또는 .java 파일)로 구성되어있습니다. 안드로이드 스튜디오에서 새로운 프로젝트를 만들면 기본으로 생성되는 activity_main.xmlMainActivity.kt이 바로 그것입니다.

# 액티비티의 상태

어플리케이션이 실행되는 동안 액티비티는 다음과 같은 상태를 거칩니다.

# Active / Running

액티비티가 태스크의 가장 위에 있고, 화면에서 볼 수 있으며, 사용자와 상호작용할 수 있는 상태입니다.

# Paused

액티비티를 화면에서 볼 수 있지만 사용자와 상호작용할 수 없는 상태입니다. 다른 액티비티가 이 액티비티를 부분적으로 가리고 있는 경우입니다.

# Stopped

다른 액티비티가 화면 전체를 가려 사용자가 볼 수 없는 상태입니다.

# Killed

안드로이드 운영체제가 액티비티를 메모리에서 해제한 상태입니다.

# 액티비티 생명주기 메소드

Activity 클래스에는 액티비티의 상태가 변할 때 호출되는 생명주기 메소드(Lifecycle Method)가 정의되어있습니다.

안드로이드 운영체제는 액티비티의 상태가 변하면 생명주기 메소드를 호출합니다.

# onCreate()

액티비티가 생성될 때 호출되며, 보통 다음과 같은 작업을 수행합니다.

  • 사용자 인터페이스 선언
  • ViewModel 초기화
  • 데이터 바인딩
  • 기타 초기화 작업

예제 코드를 살펴봅시다.

class MainActivity: AppCompatActivity() {

    lateinit var viewModel: MainViewModel

    lateinit var button: Button
    lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 사용자 인터페이스 선언
        setContentView(R.layout.activity_main)

        // ViewModel 초기화
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)

        // 데이터 바인딩
        button = findViewById(R.id.button)
        textView = findViewById(R.id.textview)
    }
}

# onstart()

액티비티가 화면에 표시될 때 호출됩니다. 보통 이 메소드에서 UI 작업을 수행합니다.

class MainActivity: AppCompatActivity() {

    lateinit var button: Button
    lateinit var textView: TextView

    // ...

    override fun onStart() {
        super.onStart()

        button.setBackgroundColor(Color.parseColor("#2596be"))
        button.setTextColor(Color.parseColor("#eab676"))

        textView.text = "Hello World"
        textView.hint = "textView"
    }
}

# onResume()

액티비티가 사용자와 상호작용할 수 있을 때 호출됩니다.

class MainActivity: AppCompatActivity() {

    // ...

    override fun onResume() {
        super.onResume()
    }
}

# onPause()

액티비티가 사용자와 상호작용할 수 없을 때 호출됩니다. 보통 다른 액티비티가 이 액티비티의 일부를 가려서 포커스를 잃었을 때 호출됩니다.

class MainActivity: AppCompatActivity() {

    // ...

    override fun onResume() {
        super.onResume()
    }
}

# onStop()

다른 액티비티가 이 액티비티 전체를 가려 화면이 보이지 않을 때 호출됩니다.

class MainActivity: AppCompatActivity() {

    // ...

    override fun onStop() {
        super.onStop()
    }
}

# onDestroy()

액티비티가 메모리에서 해제될 때 호출됩니다. 보통 onCreate()에서 초기화했던 자원을 해제하는데 사용합니다.

class MainActivity: AppCompatActivity() {

    // ...

    override fun onDestroy() {
        super.onDestroy()
    }
}

# onRestart()

정지(Stopped)된 액티비티가 다시 시작되기 전에 호출됩니다.

class MainActivity: AppCompatActivity() {

    // ...

    override fun onRestart() {
        super.onRestart()
    }
}

# 예제

이제 다양한 상황에서 액티비티 생명주기 메소드가 어떻게 호출되는지 알아보기 위해 예제 프로젝트를 만듭니다.

코드는 다음과 같습니다.

// AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yologger.lifecycle">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Lifecycle">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".SubActivity" />
        <activity android:name=".PopUpActivity"
            android:theme="@android:style/Theme.Dialog"/>
    </application>
</manifest>
// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/activity_main_tv_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is MainActivity"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/activity_main_btn_open_pop_up_activity"/>

    <Button
        android:id="@+id/activity_main_btn_open_pop_up_activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Open PopUpActivity"
        android:textAllCaps="false"
        app:layout_constraintTop_toBottomOf="@+id/activity_main_tv_label"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/activity_main_btn_open_sub_activity"/>

    <Button
        android:id="@+id/activity_main_btn_open_sub_activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Open SubActivity"
        android:textAllCaps="false"
        app:layout_constraintTop_toBottomOf="@+id/activity_main_btn_open_pop_up_activity"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
// MainActivity.kt 
class MainActivity : AppCompatActivity() {

    lateinit var textViewLabel: TextView
    lateinit var buttonOpenPopUpActivity: Button
    lateinit var buttonOpenSubActivity: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        println("[MainActivity] onCreate()")

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        buttonOpenPopUpActivity = findViewById(R.id.activity_main_btn_open_pop_up_activity)
        buttonOpenSubActivity = findViewById(R.id.activity_main_btn_open_sub_activity)

        buttonOpenPopUpActivity.setOnClickListener {
            val nextIntent = Intent(this, PopUpActivity::class.java)
            startActivity(nextIntent)
        }

        buttonOpenSubActivity.setOnClickListener {
            val nextIntent = Intent(this, SubActivity::class.java)
            startActivity(nextIntent)
        }
    }

    override fun onStart() {
        super.onStart()
        println("[MainActivity] onStart()")
    }

    override fun onResume() {
        super.onResume()
        println("[MainActivity] onResume()")
    }

    override fun onPause() {
        super.onPause()
        println("[MainActivity] onPause()")
    }

    override fun onStop() {
        super.onStop()
        println("[MainActivity] onStop()")
    }

    override fun onDestroy() {
        super.onDestroy()
        println("[MainActivity] onDestroy()")
    }
}
// activity_sub.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SubActivity">

    <TextView
        android:id="@+id/activity_sub_tv_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is SubActivity"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/activity_sub_btn_close"/>

    <Button
        android:id="@+id/activity_sub_btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Close"
        app:layout_constraintTop_toBottomOf="@+id/activity_sub_tv_label"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
// SubActivity.kt
class SubActivity : AppCompatActivity() {

    lateinit var buttonClose: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        println("[SubActivity] onCreate()")
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sub)

        buttonClose = findViewById(R.id.activity_sub_btn_close)

        buttonClose.setOnClickListener {
            finish()
        }
    }

    override fun onStart() {
        super.onStart()
        println("[SubActivity] onStart()")
    }

    override fun onResume() {
        super.onResume()
        println("[SubActivity] onResume()")
    }

    override fun onPause() {
        super.onPause()
        println("[SubActivity] onPause()")
    }

    override fun onStop() {
        super.onStop()
        println("[SubActivity] onStop()")
    }

    override fun onDestroy() {
        super.onDestroy()
        println("[SubActivity] onDestroy()")
    }
}
// activity_pop_up.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="300dp"
    android:layout_height="500dp"
    android:background="@color/white"
    tools:context=".PopUpActivity">


    <TextView
        android:id="@+id/activity_pop_up_tv_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is PopUpActivity"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/activity_pop_up_btn_close"/>

    <Button
        android:id="@+id/activity_pop_up_btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Close"
        app:layout_constraintTop_toBottomOf="@+id/activity_pop_up_tv_label"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
// PopUpActivity.kt
class PopUpActivity : Activity() {

    lateinit var buttonClose: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        println("[PopUpActivity] onStart()")
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(R.layout.activity_pop_up)

        buttonClose = findViewById(R.id.activity_pop_up_btn_close)

        buttonClose.setOnClickListener {
            finish()
        }
    }

    override fun onStart() {
        super.onStart()
        println("[PopUpActivity] onStart()")
    }

    override fun onResume() {
        super.onResume()
        println("[PopUpActivity] onResume()")
    }

    override fun onPause() {
        super.onPause()
        println("[PopUpActivity] onPause()")
    }

    override fun onStop() {
        super.onStop()
        println("[PopUpActivity] onStop()")
    }

    override fun onDestroy() {
        super.onDestroy()
        println("[PopUpActivity] onDestroy()")
    }
}

이제 앱을 실행하면 루트 액티비티인 MainActivity가 화면에 나오고 다음과 같이 출력됩니다.

[MainActivity] onCreate()
[MainActivity] onStart()
[MainActivity] onResume()

안드로이드 운영체제는 하단에 세 개의 소프트웨어 버튼을 제공합니다. 이 영역을 Soft Keys라고 부릅니다.

홈 버튼을 누르면 어떻게 될까요?

[MainActivity] onPause()
[MainActivity] onStop()

다시 앱으로 돌아오면 다음과 같이 출력됩니다.

[MainActivity] onRestart()
[MainActivity] onStart()
[MainActivity] onResume()

최근 앱 목록 버튼을 눌러도 다음과 같이 출력됩니다.

[MainActivity] onPause()
[MainActivity] onStop()

다시 앱으로 돌아오면 다음과 같이 출력됩니다.

[MainActivity] onRestart()
[MainActivity] onStart()
[MainActivity] onResume()

백 버튼을 눌러봅시다.

앱이 종료되고 다음과 같이 출력됩니다.

[MainActivity] onStart()
[MainActivity] onResume()
[MainActivity] onDestroy()

다시 앱을 실행하면 다음과 같이 출력됩니다.

[MainActivity] onCreate()
[MainActivity] onStart()
[MainActivity] onResume()

이제 SubActivity를 실행해봅시다.

[MainActivity] onPause()
[SubActivity] onCreate()
[SubActivity] onStart()
[SubActivity] onResume()
[MainActivity] onStop()

CLOSE버튼을 눌러 SubActivity를 종료해봅시다.

[SubActivity] onPause()
[MainActivity] onRestart()
[MainActivity] onStart()
[MainActivity] onResume()
[SubActivity] onStop()
[SubActivity] onDestroy()

이번엔 화면의 일부만 가리는 PopUpActivity를 호출해봅시다.

[MainActivity] onPause()
[PopUpActivity] onCreate()
[PopUpActivity] onStart()
[PopUpActivity] onResume()

이때 MainActivity는 onStop()이 아닌 onPause()까지만 호출됩니다. 이제 PopUpActivity를 닫아봅시다.

[PopUpActivity] onPause()
[MainActivity] onResume()
[PopUpActivity] onStop()
[PopUpActivity] onDestroy()